﻿using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml.Linq;

namespace Silverlight.Navigation.Mapping
{
    /// <summary>
    /// Represents a static application map defined in an xml file.
    /// </summary>
    public class XmlApplicationMap : ApplicationMap
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the XmlApplicatinMap class.
        /// </summary>
        /// <param name="filePath">The path to the xml file used to build the application map.</param>
        public XmlApplicationMap(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
                throw new ArgumentNullException("filePath");

            this.FilePath = filePath;
            LoadMap();
        }

        #endregion

        #region Properties

        /// <summary>
        /// The path to the xml file used to build the application map.
        /// </summary>
        public string FilePath { get; set; }

        /// <summary>
        /// The home menu item for the application.
        /// </summary>
        public override HomeMenuItem HomeMenuItem { get; protected set; }
        
        /// <summary>
        /// The main menu items of the application.
        /// </summary>
        public override MainMenuItemCollection MainMenuItems { get; protected set; }

        /// <summary>
        /// The uri of the image sued to separate items in the path to the current location
        /// in the application.
        /// </summary>
        public override string PathSeparatorImageUri { get; protected set; }

        /// <summary>
        /// Text used to separate items in the path to the current location in the applciation.
        /// </summary>
        public override string PathSeparatorText { get; protected set; }

        #endregion

        #region Methods

        /// <summary>
        /// Loads the map from an the xml file specified by the FilePath property.
        /// </summary>
        private void LoadMap()
        {
            XElement root = XElement.Load(FilePath);

            this.HomeMenuItem = new HomeMenuItem();
            this.HomeMenuItem.ContentUri = GetHomeContentUri(root);
            this.HomeMenuItem.Description = GetHomeDescription(root);
            this.HomeMenuItem.ImageUri = GetHomeImageUri(root);
            this.HomeMenuItem.Title = GetHomeTitle(root);

            this.PathSeparatorImageUri = GetPathSeparatorImageUri(root);
            this.PathSeparatorText = GetPathSeparatorText(root);

            var applicationMapElements = from e in root.Elements()
                                         select e;

            foreach (XElement applicationMapElement in applicationMapElements)
                this.MainMenuItems.Add(ProcessApplicationMapElement(applicationMapElement));
        }

        /// <summary>
        /// Processes the application map element.
        /// </summary>
        /// <param name="applicationMapElement">The application map element.</param>
        /// <returns></returns>
        /// <remarks>
        /// This method creates a new SubMenuItem object and gets its Title,
        /// Description, and ContentUri properties from attributes in the specified element.
        /// </remarks>
        private MainMenuItem ProcessApplicationMapElement(XElement applicationMapElement)
        {
            MainMenuItem mainMenuItem = new MainMenuItem();

            mainMenuItem.Description = GetDescription(applicationMapElement);
            mainMenuItem.ImageUri = GetImageUri(applicationMapElement);
            mainMenuItem.Title = GetTitle(applicationMapElement);

            foreach (XElement child in applicationMapElement.Elements())
            {
                SubMenuItem subMenuItem = new SubMenuItem();
                subMenuItem.Parent = mainMenuItem;

                subMenuItem.ContentUri = GetContentUri(child);
                subMenuItem.Description = GetDescription(child);
                subMenuItem.ImageUri = GetImageUri(child);
                subMenuItem.Title = GetTitle(child);

                mainMenuItem.SubMenus.Add(subMenuItem);
            }

            return mainMenuItem;
        }

        /// <summary>
        /// Gets the home content uri for the application.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns></returns>
        /// <remarks>
        /// This method interrogates the element for a homeContentUri attribute and returns the
        /// value of the attribute if it's found, otherwise an empty string is returned.
        /// </remarks>
        private string GetHomeContentUri(XElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            return GetAttributeValue(element, "homeContentUri");
        }

        /// <summary>
        /// Gets the home title uri for the application.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns></returns>
        /// This method interrogates the element for a homeTitle attribute and returns the
        /// value of the attribute if it's found, otherwise an empty string is returned.
        /// </remarks>
        private string GetHomeTitle(XElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            return GetAttributeValue(element, "homeTitle");
        }

        private string GetHomeDescription(XElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            return GetAttributeValue(element, "homeDescription");
        }

        /// <summary>
        /// Gets the home image uri.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns></returns>
        /// <remarks>
        /// This method interrogates the element for a homeImageUir attribute and returns the
        /// value of the attribute if it's found, otherwise and empty string is returned.
        /// </remarks>
        private string GetHomeImageUri(XElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            return GetAttributeValue(element, "homeImageUri");
        }

        /// <summary>
        /// Gets the image uri.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns></returns>
        /// <remarks>
        /// This method interrogates the element for an imageUri attribute and returns the
        /// value of the attribute if it's found, othewise and empty string is returned.
        /// </remarks>
        private string GetImageUri(XElement element)
        {
            if (element == null)
                throw new ArgumentException("element");

            return GetAttributeValue(element, "imageUri");
        }

        /// <summary>
        /// Gets the path separator image uri.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns></returns>
        /// <remarks>
        /// This method interrogates the element for a pathSeparatorImageUri attibute and
        /// returns the vlaue of the attribute if it's found, otherwise an empty string is returned.
        /// </remarks>
        private string GetPathSeparatorImageUri(XElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            return GetAttributeValue(element, "pathSeparatorImageUri");
        }

        /// <summary>
        /// Gets the path separator text.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns></returns>
        /// <remarks>
        /// This method interrogates the element for a pathSeparator attribute and returns
        /// the value of the attribute if it's found, otherwise an empty string is returned.
        /// </remarks>
        private string GetPathSeparatorText(XElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            return GetAttributeValue(element, "pathSeparatorText");
        }

        /// <summary>
        /// Gets the title.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns></returns>
        /// <remarks>
        /// This method interrogates the element for a title attribute and returns
        /// the value of the attribute if it's found, otherwise and empty string is returned.
        /// </remarks>
        private string GetTitle(XElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            return GetAttributeValue(element, "title");
        }

        /// <summary>
        /// Gets the description.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns></returns>
        /// <remarks>
        /// This method interrogates the element for a description attribute and returns
        /// the value of the attribute if it's found, otherwise and empty string is returned.
        /// </remarks>
        private string GetDescription(XElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            return GetAttributeValue(element, "description");
        }

        /// <summary>
        /// Gets the content uri.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns></returns>
        /// <remarks>
        /// This method interrogates the element for a uri attribute and returns
        /// the value of the attribute if it's found, otherwise and empty string is returned.
        /// </remarks>
        private string GetContentUri(XElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            return GetAttributeValue(element, "contentUri");
        }

        /// <summary>
        /// Gets the attribute value.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="attributeName">Name of the attribute.</param>
        /// <returns></returns>
        /// <remarks>
        /// This method interrogates the element for the specified attribute and returns
        /// the value of the attibute if it's found, otherwsie and empty string is returned.
        /// </remarks>
        private string GetAttributeValue(XElement element, string attributeName)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            if (string.IsNullOrEmpty(attributeName))
                throw new ArgumentNullException("attributeName");

            var attribute = (from a in element.Attributes()
                             where a.Name == attributeName
                             select a).SingleOrDefault();

            return attribute != null ? attribute.Value : string.Empty;
        }

        #endregion
    }
}
